<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />

    

    
    <title>【React深入】从Mixin到HOC再到Hook | 李世奇的技术专栏</title>
    
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    
        <meta name="keywords" content="React" />
    
    <meta name="description" content="导读前端发展速度非常之快，页面和组件变得越来越复杂，如何更好的实现状态逻辑复用一直都是应用程序中重要的一部分，这直接关系着应用程序的质量以及维护的难易程度。 本文介绍了React采用的三种实现状态逻辑复用的技术，并分析了他们的实现原理、使用方法、实际应用以及如何选择使用他们。 本文略长，下面是本文的思维导图，您可以从头开始阅读，也可以选择感兴趣的部分阅读：  Mixin设计模式 Mixin（混入">
<meta name="keywords" content="React">
<meta property="og:type" content="article">
<meta property="og:title" content="【React深入】从Mixin到HOC再到Hook">
<meta property="og:url" content="https://www.lisq.xyz/2019/04/09/【React深入】从Mixin到HOC再到Hook/index.html">
<meta property="og:site_name" content="李世奇的技术专栏">
<meta property="og:description" content="导读前端发展速度非常之快，页面和组件变得越来越复杂，如何更好的实现状态逻辑复用一直都是应用程序中重要的一部分，这直接关系着应用程序的质量以及维护的难易程度。 本文介绍了React采用的三种实现状态逻辑复用的技术，并分析了他们的实现原理、使用方法、实际应用以及如何选择使用他们。 本文略长，下面是本文的思维导图，您可以从头开始阅读，也可以选择感兴趣的部分阅读：  Mixin设计模式 Mixin（混入">
<meta property="og:locale" content="zh-CN">
<meta property="og:image" content="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/hoc9.png">
<meta property="og:updated_time" content="2019-04-09T15:28:00.607Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="【React深入】从Mixin到HOC再到Hook">
<meta name="twitter:description" content="导读前端发展速度非常之快，页面和组件变得越来越复杂，如何更好的实现状态逻辑复用一直都是应用程序中重要的一部分，这直接关系着应用程序的质量以及维护的难易程度。 本文介绍了React采用的三种实现状态逻辑复用的技术，并分析了他们的实现原理、使用方法、实际应用以及如何选择使用他们。 本文略长，下面是本文的思维导图，您可以从头开始阅读，也可以选择感兴趣的部分阅读：  Mixin设计模式 Mixin（混入">
<meta name="twitter:image" content="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/hoc9.png">
    

    

    

    <link rel="stylesheet" href="/libs/font-awesome/css/font-awesome.min.css">
    <link rel="stylesheet" href="/libs/titillium-web/styles.css">
    <link rel="stylesheet" href="/libs/source-code-pro/styles.css">

    <link rel="stylesheet" href="/css/style.css">

    <script src="/libs/jquery/3.3.1/jquery.min.js"></script>
    
    
        <link rel="stylesheet" href="/libs/lightgallery/css/lightgallery.min.css">
    
    
        <link rel="stylesheet" href="/libs/justified-gallery/justifiedGallery.min.css">
    
    
    


</head>


<body>
    <div id="wrap">
        <header id="header">
    <div id="header-outer" class="outer">
        <div class="container">
            <div class="container-inner">
                <div id="header-title">
                    <h1 class="logo-wrap">
                        <a href="/" class="logo"></a>
                    </h1>
                    
                </div>
                <div id="header-inner" class="nav-container">
                    <a id="main-nav-toggle" class="nav-icon fa fa-bars"></a>
                    <div class="nav-container-inner">
                        <ul id="main-nav">
                            
                                <li class="main-nav-list-item" >
                                    <a class="main-nav-list-link" href="/">主页</a>
                                </li>
                            
                                        
                                    
                                <li class="main-nav-list-item" >
                                    <a class="main-nav-list-link" href="https://mp.weixin.qq.com/s/dYZEHTgqvxGV7mL99JuxRQ">关于</a>
                                </li>
                            
                                <li class="main-nav-list-item" >
                                    <a class="main-nav-list-link" href="https://github.com/ConardLi/ConardLi.github.io">更新日志</a>
                                </li>
                            
                                <li class="main-nav-list-item" >
                                    <a class="main-nav-list-link" href="/me/navigation.html">index.导航</a>
                                </li>
                            
                        </ul>
                        <nav id="sub-nav">
                            <div id="search-form-wrap">

    <form class="search-form">
        <input type="text" class="ins-search-input search-form-input" placeholder="搜索" />
        <button type="submit" class="search-form-submit"></button>
    </form>
    <div class="ins-search">
    <div class="ins-search-mask"></div>
    <div class="ins-search-container">
        <div class="ins-input-wrapper">
            <input type="text" class="ins-search-input" placeholder="想要查找什么..." />
            <span class="ins-close ins-selectable"><i class="fa fa-times-circle"></i></span>
        </div>
        <div class="ins-section-wrapper">
            <div class="ins-section-container"></div>
        </div>
    </div>
</div>
<script>
(function (window) {
    var INSIGHT_CONFIG = {
        TRANSLATION: {
            POSTS: '文章',
            PAGES: '页面',
            CATEGORIES: '分类',
            TAGS: '标签',
            UNTITLED: '(未命名)',
        },
        ROOT_URL: '/',
        CONTENT_URL: '/content.json',
    };
    window.INSIGHT_CONFIG = INSIGHT_CONFIG;
})(window);
</script>
<script src="/js/insight.js"></script>

</div>
                        </nav>
                    </div>
                </div>
            </div>
        </div>
    </div>
</header>
        <div class="container">
            <div class="main-body container-inner">
                <div class="main-body-inner">
                    <section id="main">
                        <div class="main-body-header">
    <h1 class="header">
    
    未分类
    </h1>
</div>

                        <div class="main-body-content">
                            <style>
    #article-toc.fixed {
        position: fixed;
        top: 30px;
        bottom: 30px;
        left: 32px;
        padding-top: 15px;
        z-index: 9999;
        background-color: antiquewhite;
        width: 200px;
        padding-left: 20px;
        padding-bottom: 30px;
        border-radius: 10px;
        border: 1px solid rgb(122, 122, 122);
        opacity: 0.8;
    }

    #article-toc-inner {
        padding: 10px;
        width: 200px;
        height: 100%;
        overflow: auto;
    }

    .toc-level-3 {
        margin-left: 20px;
    }

    #article-toc a {
        text-decoration: none;
        color: #333;
        padding: 3px 0;
        color: #000;
        font-weight: normal;
    }

    .toc-item {
        line-height: 30px;
        list-style-type: initial;
    }

    #tocbtn {
        position: fixed;
        top: 30%;
        left: 0px;
        padding: 5px 2px;
        width: 18px;
        word-wrap: break-word;
        background-color: rgb(242, 245, 247);
        box-shadow: rgba(118, 118, 118, 0.11) 2px 0px 5px 0px;
        opacity: 1;
        height: 94px;
        line-height: 16px;
        text-align: center;
        top: 33%;
        width: 30px;
        z-index: 1000000001;
        cursor: pointer;
        border-radius: 0px 4px 4px 0px;
        border-width: 1px 1px 1px;
        border-style: solid solid solid none;
        border-color: rgb(224, 228, 231) rgb(224, 228, 231) rgb(224, 228, 231);
        border-image: initial;
        border-left: none;
        padding: 6px;
        transition: right 0.4s ease-in 0.3s, opacity 0.5s ease-in 0.3s;
    }
</style>

<article id="post-【React深入】从Mixin到HOC再到Hook"
    class="article article-single article-type-post"
    itemscope itemprop="blogPost">
    <div class="article-inner">
        
        <header class="article-header">
            
    
        <h1 class="article-title" itemprop="name">
        【React深入】从Mixin到HOC再到Hook
        </h1>
    

        </header>
        
        
        <div class="article-meta">
            
    <div class="article-date">
        <a href="/2019/04/09/【React深入】从Mixin到HOC再到Hook/" class="article-date">
            <time datetime="2019-04-09T15:27:27.000Z" itemprop="datePublished">2019-04-09</time>
        </a>
    </div>

            

            
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/React/">React</a>
    </div>

            <span id="busuanzi_container_page_pv" style="position: relative;float: right;">
                本文总阅读量<span id="busuanzi_value_page_pv"></span>次
            </span>
        </div>
        
        
        <div class="article-entry" itemprop="articleBody">
            <p><img src="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/hoc9.png" alt="image"></p>
<h2 id="导读"><a href="#导读" class="headerlink" title="导读"></a>导读</h2><p>前端发展速度非常之快，页面和组件变得越来越复杂，如何更好的实现<code>状态逻辑复用</code>一直都是应用程序中重要的一部分，这直接关系着应用程序的质量以及维护的难易程度。</p>
<p>本文介绍了<code>React</code>采用的三种实现<code>状态逻辑复用</code>的技术，并分析了他们的实现原理、使用方法、实际应用以及如何选择使用他们。</p>
<p>本文略长，下面是本文的思维导图，您可以从头开始阅读，也可以选择感兴趣的部分阅读：</p>
<p><img src="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/hook2.png" alt="image"></p>
<h2 id="Mixin设计模式"><a href="#Mixin设计模式" class="headerlink" title="Mixin设计模式"></a>Mixin设计模式</h2><p><img src="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/mixin.png" alt="image"></p>
<p><code>Mixin</code>（混入）是一种通过扩展收集功能的方式，它本质上是将一个对象的属性拷贝到另一个对象上面去，不过你可以拷贝<code>任意多</code>个对象的<code>任意个</code>方法到一个新对象上去，这是<code>继承</code>所不能实现的。它的出现主要就是为了解决代码复用问题。</p>
<p>很多开源库提供了<code>Mixin</code>的实现，如<code>Underscore</code>的<code>_.extend</code>方法、<code>JQuery</code>的<code>extend</code>方法。</p>
<p>使用<code>_.extend</code>方法实现代码复用：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> LogMixin = &#123;</span><br><span class="line">  actionLog: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'action...'</span>);</span><br><span class="line">  &#125;,</span><br><span class="line">  requestLog: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'request...'</span>);</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">User</span>(<span class="params"></span>) </span>&#123;  <span class="comment">/*..*/</span>  &#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Goods</span>(<span class="params"></span>) </span>&#123;  <span class="comment">/*..*/</span> &#125;</span><br><span class="line">_.extend(User.prototype, LogMixin);</span><br><span class="line">_.extend(Goods.prototype, LogMixin);</span><br><span class="line"><span class="keyword">var</span> user = <span class="keyword">new</span> User();</span><br><span class="line"><span class="keyword">var</span> good = <span class="keyword">new</span> Goods();</span><br><span class="line">user.actionLog();</span><br><span class="line">good.requestLog();</span><br></pre></td></tr></table></figure>
<p>我们可以尝试手动写一个简单的<code>Mixin</code>方法：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">setMixin</span>(<span class="params">target, mixin</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (<span class="built_in">arguments</span>[<span class="number">2</span>]) &#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">2</span>, len = <span class="built_in">arguments</span>.length; i &lt; len; i++) &#123;</span><br><span class="line">      target.prototype[<span class="built_in">arguments</span>[i]] = mixin.prototype[<span class="built_in">arguments</span>[i]];</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> methodName <span class="keyword">in</span> mixin.prototype) &#123;</span><br><span class="line">      <span class="keyword">if</span> (!<span class="built_in">Object</span>.hasOwnProperty(target.prototype, methodName)) &#123;</span><br><span class="line">        target.prototype[methodName] = mixin.prototype[methodName];</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">setMixin(User,LogMixin,<span class="string">'actionLog'</span>);</span><br><span class="line">setMixin(Goods,LogMixin,<span class="string">'requestLog'</span>);</span><br></pre></td></tr></table></figure>
<p>您可以使用<code>setMixin</code>方法将任意对象的任意方法扩展到目标对象上。</p>
<h2 id="React中应用Mixin"><a href="#React中应用Mixin" class="headerlink" title="React中应用Mixin"></a>React中应用Mixin</h2><p><code>React</code>也提供了<code>Mixin</code>的实现，如果完全不同的组件有相似的功能，我们可以引入来实现代码复用，当然只有在使用<code>createClass</code>来创建<code>React</code>组件时才可以使用，因为在<code>React</code>组件的<code>es6</code>写法中它已经被废弃掉了。</p>
<p>例如下面的例子，很多组件或页面都需要记录用户行为，性能指标等。如果我们在每个组件都引入写日志的逻辑，会产生大量重复代码，通过<code>Mixin</code>我们可以解决这一问题：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> LogMixin = &#123;</span><br><span class="line">  log: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'log'</span>);</span><br><span class="line">  &#125;,</span><br><span class="line">  componentDidMount: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'in'</span>);</span><br><span class="line">  &#125;,</span><br><span class="line">  componentWillUnmount: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'out'</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> User = React.createClass(&#123;</span><br><span class="line">  mixins: [LogMixin],</span><br><span class="line">  render: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> (<span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>...<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> Goods = React.createClass(&#123;</span><br><span class="line">  mixins: [LogMixin],</span><br><span class="line">  render: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> (<span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>...<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h2 id="Mixin带来的危害"><a href="#Mixin带来的危害" class="headerlink" title="Mixin带来的危害"></a>Mixin带来的危害</h2><p><code>React</code>官方文档在<a href="https://react.docschina.org/blog/2016/07/13/mixins-considered-harmful.html" target="_blank" rel="noopener">Mixins Considered Harmful</a>一文中提到了<code>Mixin</code>带来了危害：</p>
<ul>
<li><code>Mixin</code> 可能会相互依赖，相互耦合，不利于代码维护</li>
<li>不同的<code>Mixin</code>中的方法可能会相互冲突</li>
<li><code>Mixin</code>非常多时，组件是可以感知到的，甚至还要为其做相关处理，这样会给代码造成滚雪球式的复杂性</li>
</ul>
<p><code>React</code>现在已经不再推荐使用<code>Mixin</code>来解决代码复用问题，因为<code>Mixin</code>带来的危害比他产生的价值还要巨大，并且<code>React</code>全面推荐使用高阶组件来替代它。另外，高阶组件还能实现更多其他更强大的功能，在学习高阶组件之前，我们先来看一个设计模式。</p>
<h2 id="装饰模式"><a href="#装饰模式" class="headerlink" title="装饰模式"></a>装饰模式</h2><p><img src="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/decorator.png" alt="image"></p>
<p>装饰者(<code>decorator</code>)模式能够在不改变对象自身的基础上，在程序运行期间给对像动态的添加职责。与继承相比，装饰者是一种更轻便灵活的做法。</p>
<h2 id="高阶组件（HOC）"><a href="#高阶组件（HOC）" class="headerlink" title="高阶组件（HOC）"></a>高阶组件（HOC）</h2><p><img src="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/hoc9.png" alt="image"></p>
<p>高阶组件可以看作<code>React</code>对装饰模式的一种实现，高阶组件就是一个函数，且该函数接受一个组件作为参数，并返回一个新的组件。</p>
<blockquote>
<p>高阶组件（<code>HOC</code>）是<code>React</code>中的高级技术，用来重用组件逻辑。但高阶组件本身并不是<code>React API</code>。它只是一种模式，这种模式是由<code>React</code>自身的组合性质必然产生的。</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">visible</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; visible, ...props &#125; = <span class="keyword">this</span>.props;</span><br><span class="line">      <span class="keyword">if</span> (visible === <span class="literal">false</span>) <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> &#123;<span class="attr">...props</span>&#125; /&gt;</span>;</span></span><br><span class="line"><span class="xml">    &#125;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br></pre></td></tr></table></figure>
<p>上面的代码就是一个<code>HOC</code>的简单应用，函数接收一个组件作为参数，并返回一个新组件，新组建可以接收一个<code>visible props</code>，根据<code>visible</code>的值来判断是否渲染Visible。</p>
<p>下面我们从以下几方面来具体探索<code>HOC</code>。</p>
<p><img src="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/hoc8.png" alt="image"></p>
<h2 id="HOC的实现方式"><a href="#HOC的实现方式" class="headerlink" title="HOC的实现方式"></a>HOC的实现方式</h2><h3 id="属性代理"><a href="#属性代理" class="headerlink" title="属性代理"></a>属性代理</h3><p>函数返回一个我们自己定义的组件，然后在<code>render</code>中返回要包裹的组件，这样我们就可以代理所有传入的<code>props</code>，并且决定如何渲染，实际上 ，这种方式生成的高阶组件就是原组件的父组件，上面的函数<code>visible</code>就是一个<code>HOC</code>属性代理的实现方式。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">proxyHOC</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> &#123;<span class="attr">...this.props</span>&#125; /&gt;</span>;</span></span><br><span class="line"><span class="xml">    &#125;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br></pre></td></tr></table></figure>
<p>对比原生组件增强的项：</p>
<ul>
<li>可操作所有传入的<code>props</code></li>
<li>可操作组件的生命周期</li>
<li>可操作组件的<code>static</code>方法</li>
<li>获取<code>refs</code></li>
</ul>
<h3 id="反向继承"><a href="#反向继承" class="headerlink" title="反向继承"></a>反向继承</h3><p>返回一个组件，继承原组件，在<code>render</code>中调用原组件的<code>render</code>。由于继承了原组件，能通过this访问到原组件的<code>生命周期、props、state、render</code>等，相比属性代理它能操作更多的属性。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">inheritHOC</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">WrappedComponent</span> </span>&#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">super</span>.render();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>对比原生组件增强的项：</p>
<ul>
<li>可操作所有传入的<code>props</code></li>
<li>可操作组件的生命周期</li>
<li>可操作组件的<code>static</code>方法</li>
<li>获取<code>refs</code></li>
<li>可操作<code>state</code></li>
<li>可以渲染劫持</li>
</ul>
<h2 id="HOC可以实现什么功能"><a href="#HOC可以实现什么功能" class="headerlink" title="HOC可以实现什么功能"></a>HOC可以实现什么功能</h2><h3 id="组合渲染"><a href="#组合渲染" class="headerlink" title="组合渲染"></a>组合渲染</h3><p>可使用任何其他组件和原组件进行组合渲染，达到样式、布局复用等效果。</p>
<blockquote>
<p>通过属性代理实现</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">stylHOC</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">return</span> (<span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">"title"</span>&gt;</span>&#123;this.props.title&#125;<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">        &lt;WrappedComponent &#123;...this.props&#125; /&gt;</span><br><span class="line"><span class="xml">      <span class="tag">&lt;/<span class="name">div</span>&gt;</span>);</span></span><br><span class="line"><span class="xml">    &#125;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>通过反向继承实现</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">styleHOC</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">WrappedComponent</span> </span>&#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">"title"</span>&gt;</span>&#123;this.props.title&#125;<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml">        &#123;super.render()&#125;</span></span><br><span class="line"><span class="xml">      <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="条件渲染"><a href="#条件渲染" class="headerlink" title="条件渲染"></a>条件渲染</h3><p>根据特定的属性决定原组件是否渲染</p>
<blockquote>
<p>通过属性代理实现</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">visibleHOC</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">this</span>.props.visible === <span class="literal">false</span>) <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> &#123;<span class="attr">...props</span>&#125; /&gt;</span>;</span></span><br><span class="line"><span class="xml">    &#125;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>通过反向继承实现</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">visibleHOC</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">WrappedComponent</span> </span>&#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">this</span>.props.visible === <span class="literal">false</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span></span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">super</span>.render()</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="操作props"><a href="#操作props" class="headerlink" title="操作props"></a>操作props</h3><p>可以对传入组件的<code>props</code>进行增加、修改、删除或者根据特定的<code>props</code>进行特殊的操作。</p>
<blockquote>
<p>通过属性代理实现</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">proxyHOC</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">const</span> newProps = &#123;</span><br><span class="line">        ...this.props,</span><br><span class="line">        user: <span class="string">'ConardLi'</span></span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> &#123;<span class="attr">...newProps</span>&#125; /&gt;</span>;</span></span><br><span class="line"><span class="xml">    &#125;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br></pre></td></tr></table></figure>
<h3 id="获取refs"><a href="#获取refs" class="headerlink" title="获取refs"></a>获取refs</h3><p>高阶组件中可获取原组件的<code>ref</code>，通过<code>ref</code>获取组件实力，如下面的代码，当程序初始化完成后调用原组件的log方法。(不知道refs怎么用，请👇<a href="https://react.docschina.org/docs/refs-and-the-dom.html" target="_blank" rel="noopener">Refs &amp; DOM</a>)</p>
<blockquote>
<p>通过属性代理实现</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">refHOC</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">    componentDidMount() &#123;</span><br><span class="line">      <span class="keyword">this</span>.wapperRef.log()</span><br><span class="line">    &#125;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> &#123;<span class="attr">...this.props</span>&#125; <span class="attr">ref</span>=<span class="string">&#123;ref</span> =&gt;</span> &#123; this.wapperRef = ref &#125;&#125; /&gt;;</span></span><br><span class="line"><span class="xml">    &#125;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br></pre></td></tr></table></figure>
<p>这里注意：调用高阶组件的时候并不能获取到原组件的真实<code>ref</code>，需要手动进行传递，具体请看<a href="#传递refs">传递refs</a></p>
<h3 id="状态管理"><a href="#状态管理" class="headerlink" title="状态管理"></a>状态管理</h3><p>将原组件的状态提取到<code>HOC</code>中进行管理，如下面的代码，我们将<code>Input</code>的<code>value</code>提取到<code>HOC</code>中进行管理，使它变成受控组件，同时不影响它使用<code>onChange</code>方法进行一些其他操作。基于这种方式，我们可以实现一个简单的<code>双向绑定</code>，具体请看<a href="#双向绑定">双向绑定</a>。</p>
<blockquote>
<p>通过属性代理实现</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">proxyHoc</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">    <span class="keyword">constructor</span>(props) &#123;</span><br><span class="line">      <span class="keyword">super</span>(props);</span><br><span class="line">      <span class="keyword">this</span>.state = &#123; <span class="attr">value</span>: <span class="string">''</span> &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    onChange = <span class="function">(<span class="params">event</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; onChange &#125; = <span class="keyword">this</span>.props;</span><br><span class="line">      <span class="keyword">this</span>.setState(&#123;</span><br><span class="line">        value: event.target.value,</span><br><span class="line">      &#125;, () =&gt; &#123;</span><br><span class="line">        <span class="keyword">if</span>(<span class="keyword">typeof</span> onChange ===<span class="string">'function'</span>)&#123;</span><br><span class="line">          onChange(event);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">const</span> newProps = &#123;</span><br><span class="line">        value: <span class="keyword">this</span>.state.value,</span><br><span class="line">        onChange: <span class="keyword">this</span>.onChange,</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> &#123;<span class="attr">...this.props</span>&#125; &#123;<span class="attr">...newProps</span>&#125; /&gt;</span>;</span></span><br><span class="line"><span class="xml">    &#125;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br><span class="line"><span class="xml"></span></span><br><span class="line"><span class="xml">class HOC extends Component &#123;</span></span><br><span class="line"><span class="xml">  render() &#123;</span></span><br><span class="line"><span class="xml">    return <span class="tag">&lt;<span class="name">input</span> &#123;<span class="attr">...this.props</span>&#125;&gt;</span><span class="tag">&lt;/<span class="name">input</span>&gt;</span></span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br><span class="line"><span class="xml"></span></span><br><span class="line"><span class="xml">export default proxyHoc(HOC);</span></span><br></pre></td></tr></table></figure>
<h3 id="操作state"><a href="#操作state" class="headerlink" title="操作state"></a>操作state</h3><p>上面的例子通过属性代理利用HOC的state对原组件进行了一定的增强，但并不能直接控制原组件的<code>state</code>，而通过反向继承，我们可以直接操作原组件的<code>state</code>。但是并不推荐直接修改或添加原组件的<code>state</code>，因为这样有可能和组件内部的操作构成冲突。</p>
<blockquote>
<p>通过反向继承实现</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">debugHOC</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">WrappedComponent</span> </span>&#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">'props'</span>, <span class="keyword">this</span>.props);</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">'state'</span>, <span class="keyword">this</span>.state);</span><br><span class="line">      <span class="keyword">return</span> (</span><br><span class="line">        &lt;div className=<span class="string">"debuging"</span>&gt;</span><br><span class="line">          &#123;<span class="keyword">super</span>.render()&#125;</span><br><span class="line">        &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">      )</span></span><br><span class="line"><span class="regexp">    &#125;</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>
<p>上面的<code>HOC</code>在<code>render</code>中将<code>props</code>和<code>state</code>打印出来，可以用作调试阶段，当然你可以在里面写更多的调试代码。想象一下，只需要在我们想要调试的组件上加上<code>@debug</code>就可以对该组件进行调试，而不需要在每次调试的时候写很多冗余代码。(如果你还不知道怎么使用HOC，请👇<a href="#如何使用HOC">如何使用HOC</a>)</p>
<h3 id="渲染劫持"><a href="#渲染劫持" class="headerlink" title="渲染劫持"></a>渲染劫持</h3><p>高阶组件可以在render函数中做非常多的操作，从而控制原组件的渲染输出。只要改变了原组件的渲染，我们都将它称之为一种<code>渲染劫持</code>。</p>
<p>实际上，上面的<a href="#组合渲染">组合渲染</a>和<a href="#条件渲染">条件渲染</a>都是<code>渲染劫持</code>的一种，通过反向继承，不仅可以实现以上两点，还可直接<code>增强</code>由原组件<code>render</code>函数产生的<code>React元素</code>。</p>
<blockquote>
<p>通过反向继承实现</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">hijackHOC</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">WrappedComponent</span> </span>&#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">const</span> tree = <span class="keyword">super</span>.render();</span><br><span class="line">      <span class="keyword">let</span> newProps = &#123;&#125;;</span><br><span class="line">      <span class="keyword">if</span> (tree &amp;&amp; tree.type === <span class="string">'input'</span>) &#123;</span><br><span class="line">        newProps = &#123; <span class="attr">value</span>: <span class="string">'渲染被劫持了'</span> &#125;;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">const</span> props = <span class="built_in">Object</span>.assign(&#123;&#125;, tree.props, newProps);</span><br><span class="line">      <span class="keyword">const</span> newTree = React.cloneElement(tree, props, tree.props.children);</span><br><span class="line">      <span class="keyword">return</span> newTree;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>注意上面的说明我用的是<code>增强</code>而不是<code>更改</code>。<code>render</code>函数内实际上是调用<code>React.creatElement</code>产生的<code>React元素</code>：</p>
<p><img src="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/hoc1.png" alt="image"><br>虽然我们能拿到它，但是我们不能直接修改它里面的属性，我们通过<code>getOwnPropertyDescriptors</code>函数来打印下它的配置项：</p>
<p><img src="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/hoc2.png" alt="image"></p>
<p>可以发现，所有的<code>writable</code>属性均被配置为了<code>false</code>，即所有属性是不可变的。（对这些配置项有疑问，请👇<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty" target="_blank" rel="noopener">defineProperty</a>）</p>
<p>不能直接修改，我们可以借助<code>cloneElement</code>方法来在原组件的基础上增强一个新组件：</p>
<blockquote>
<p><code>React.cloneElement()</code>克隆并返回一个新的<code>React元素</code>，使用<code>element</code>作为起点。生成的元素将会拥有原始元素props与新props的浅合并。新的子级会替换现有的子级。来自原始元素的 key 和 ref 将会保留。</p>
</blockquote>
<p><code>React.cloneElement()</code>几乎相当于：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">&lt;element.type &#123;...element.props&#125; &#123;...props&#125;&gt;&#123;children&#125;&lt;<span class="regexp">/element.type&gt;</span></span><br></pre></td></tr></table></figure>
<h2 id="如何使用HOC"><a href="#如何使用HOC" class="headerlink" title="如何使用HOC"></a>如何使用HOC</h2><p>上面的示例代码都写的是如何声明一个<code>HOC</code>，<code>HOC</code>实际上是一个函数，所以我们将要增强的组件作为参数调用<code>HOC</code>函数，得到增强后的组件。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">myComponent</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> (<span class="xml"><span class="tag">&lt;<span class="name">span</span>&gt;</span>原组件<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> inheritHOC(myComponent);</span><br></pre></td></tr></table></figure>
<h3 id="compose"><a href="#compose" class="headerlink" title="compose"></a>compose</h3><p>在实际应用中，一个组件可能被多个<code>HOC</code>增强，我们使用的是被所有的<code>HOC</code>增强后的组件，借用一张<code>装饰模式</code>的图来说明，可能更容易理解：</p>
<p><img src="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/hoc3.jpeg" alt="image"></p>
<p>假设现在我们有<code>logger</code>，<code>visible</code>，<code>style</code>等多个<code>HOC</code>，现在要同时增强一个<code>Input</code>组件：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">logger(visible(style(Input)))</span><br></pre></td></tr></table></figure>
<p>这种代码非常的难以阅读，我们可以手动封装一个简单的函数组合工具，将写法改写如下：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> compose = <span class="function">(<span class="params">...fns</span>) =&gt;</span> fns.reduce(<span class="function">(<span class="params">f, g</span>) =&gt;</span> (...args) =&gt; g(f(...args)));</span><br><span class="line">compose(logger,visible,style)(Input);</span><br></pre></td></tr></table></figure>
<p><code>compose</code>函数返回一个所有函数组合后的函数，<code>compose(f, g, h)</code> 和 <code>(...args) =&gt; f(g(h(...args)))</code>是一样的。</p>
<p>很多第三方库都提供了类似<code>compose</code>的函数，例如<code>lodash.flowRight</code>，<code>Redux</code>提供的<code>combineReducers</code>函数等。</p>
<h3 id="Decorators"><a href="#Decorators" class="headerlink" title="Decorators"></a>Decorators</h3><p>我们还可以借助<code>ES7</code>为我们提供的<code>Decorators</code>来让我们的写法变的更加优雅：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">@logger</span><br><span class="line">@visible</span><br><span class="line">@style</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Input</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>Decorators</code>是<code>ES7</code>的一个提案，还没有被标准化，但目前<code>Babel</code>转码器已经支持，我们需要提前配置<code>babel-plugin-transform-decorators-legacy</code>：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="string">"plugins"</span>: [<span class="string">"transform-decorators-legacy"</span>]</span><br></pre></td></tr></table></figure>
<p>还可以结合上面的<code>compose</code>函数使用：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> hoc = compose(logger, visible, style);</span><br><span class="line">@hoc</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Input</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="HOC的实际应用"><a href="#HOC的实际应用" class="headerlink" title="HOC的实际应用"></a>HOC的实际应用</h2><p>下面是一些我在公司项目中实际对<code>HOC</code>的实际应用场景，由于文章篇幅原因，代码经过很多简化，如有问题欢迎在评论区指出：</p>
<h3 id="日志打点"><a href="#日志打点" class="headerlink" title="日志打点"></a>日志打点</h3><p>实际上这属于一类最常见的应用，多个组件拥有类似的逻辑，我们要对重复的逻辑进行复用，<br>官方文档中<code>CommentList</code>的示例也是解决了代码复用问题，写的很详细，有兴趣可以👇<a href="https://react.docschina.org/docs/higher-order-components.html#%E4%BD%BF%E7%94%A8%E9%AB%98%E9%98%B6%E7%BB%84%E4%BB%B6%EF%BC%88hoc%EF%BC%89%E8%A7%A3%E5%86%B3%E6%A8%AA%E5%88%87%E5%85%B3%E6%B3%A8%E7%82%B9" target="_blank" rel="noopener">使用高阶组件（HOC）解决横切关注点</a>。</p>
<p>某些页面需要记录用户行为，性能指标等等，通过高阶组件做这些事情可以省去很多重复代码。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">logHoc</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">    componentWillMount() &#123;</span><br><span class="line">      <span class="keyword">this</span>.start = <span class="built_in">Date</span>.now();</span><br><span class="line">    &#125;</span><br><span class="line">    componentDidMount() &#123;</span><br><span class="line">      <span class="keyword">this</span>.end = <span class="built_in">Date</span>.now();</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">`<span class="subst">$&#123;WrappedComponent.dispalyName&#125;</span> 渲染时间：<span class="subst">$&#123;<span class="keyword">this</span>.end - <span class="keyword">this</span>.start&#125;</span> ms`</span>);</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">`<span class="subst">$&#123;user&#125;</span>进入<span class="subst">$&#123;WrappedComponent.dispalyName&#125;</span>`</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    componentWillUnmount() &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">`<span class="subst">$&#123;user&#125;</span>退出<span class="subst">$&#123;WrappedComponent.dispalyName&#125;</span>`</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> &#123;<span class="attr">...this.props</span>&#125; /&gt;</span></span></span><br><span class="line"><span class="xml">    &#125;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br></pre></td></tr></table></figure>
<h3 id="可用、权限控制"><a href="#可用、权限控制" class="headerlink" title="可用、权限控制"></a>可用、权限控制</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">auth</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; visible, auth, display = <span class="literal">null</span>, ...props &#125; = <span class="keyword">this</span>.props;</span><br><span class="line">      <span class="keyword">if</span> (visible === <span class="literal">false</span> || (auth &amp;&amp; authList.indexOf(auth) === <span class="number">-1</span>)) &#123;</span><br><span class="line">        <span class="keyword">return</span> display</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> &#123;<span class="attr">...props</span>&#125; /&gt;</span>;</span></span><br><span class="line"><span class="xml">    &#125;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br></pre></td></tr></table></figure>
<p><code>authList</code>是我们在进入程序时向后端请求的所有权限列表，当组件所需要的权限不列表中，或者设置的<br><code>visible</code>是<code>false</code>，我们将其显示为传入的组件样式，或者<code>null</code>。我们可以将任何需要进行权限校验的组件应用<code>HOC</code>：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">@auth</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Input</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;  ...  &#125;</span><br><span class="line">@auth</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Button</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;  ...  &#125;</span><br><span class="line"></span><br><span class="line">&lt;Button auth=<span class="string">"user/addUser"</span>&gt;添加用户&lt;<span class="regexp">/Button&gt;</span></span><br><span class="line"><span class="regexp">&lt;Input auth="user/</span>search<span class="string">" visible=&#123;false&#125; &gt;添加用户&lt;/Input&gt;</span></span><br></pre></td></tr></table></figure>
<h3 id="双向绑定"><a href="#双向绑定" class="headerlink" title="双向绑定"></a>双向绑定</h3><p>在<code>vue</code>中，绑定一个变量后可实现双向数据绑定，即表单中的值改变后绑定的变量也会自动改变。而<code>React</code>中没有做这样的处理，在默认情况下，表单元素都是<code>非受控组件</code>。给表单元素绑定一个状态后，往往需要手动书写<code>onChange</code>方法来将其改写为<code>受控组件</code>，在表单元素非常多的情况下这些重复操作是非常痛苦的。</p>
<p>我们可以借助高阶组件来实现一个简单的双向绑定，代码略长，可以结合下面的思维导图进行理解。</p>
<p><img src="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/hoc4.png" alt="image"></p>
<p>首先我们自定义一个<code>Form</code>组件，该组件用于包裹所有需要包裹的表单组件，通过<code>contex</code>向子组件暴露两个属性：</p>
<ul>
<li><code>model</code>：当前<code>Form</code>管控的所有数据，由表单<code>name</code>和<code>value</code>组成，如<code>{name:&#39;ConardLi&#39;,pwd:&#39;123&#39;}</code>。<code>model</code>可由外部传入，也可自行管控。</li>
<li><code>changeModel</code>：改变<code>model</code>中某个<code>name</code>的值。</li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Form</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">  <span class="keyword">static</span> childContextTypes = &#123;</span><br><span class="line">    model: PropTypes.object,</span><br><span class="line">    changeModel: PropTypes.func</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">constructor</span>(props, context) &#123;</span><br><span class="line">    <span class="keyword">super</span>(props, context);</span><br><span class="line">    <span class="keyword">this</span>.state = &#123;</span><br><span class="line">      model: props.model || &#123;&#125;</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;</span><br><span class="line">  componentWillReceiveProps(nextProps) &#123;</span><br><span class="line">    <span class="keyword">if</span> (nextProps.model) &#123;</span><br><span class="line">      <span class="keyword">this</span>.setState(&#123;</span><br><span class="line">        model: nextProps.model</span><br><span class="line">      &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  changeModel = <span class="function">(<span class="params">name, value</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">this</span>.setState(&#123;</span><br><span class="line">      model: &#123; ...this.state.model, [name]: value &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">  getChildContext() &#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">      changeModel: <span class="keyword">this</span>.changeModel,</span><br><span class="line">      model: <span class="keyword">this</span>.props.model || <span class="keyword">this</span>.state.model</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;</span><br><span class="line">  onSubmit = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>.state.model);</span><br><span class="line">  &#125;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml">      &#123;this.props.children&#125;</span></span><br><span class="line"><span class="xml">      <span class="tag">&lt;<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">&#123;this.onSubmit&#125;</span>&gt;</span>提交<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>下面定义用于双向绑定的<code>HOC</code>，其代理了表单的<code>onChange</code>属性和<code>value</code>属性：</p>
<ul>
<li>发生<code>onChange</code>事件时调用上层<code>Form</code>的<code>changeModel</code>方法来改变<code>context</code>中的<code>model</code>。</li>
<li>在渲染时将<code>value</code>改为从<code>context</code>中取出的值。</li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">proxyHoc</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> contextTypes = &#123;</span><br><span class="line">      model: PropTypes.object,</span><br><span class="line">      changeModel: PropTypes.func</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    onChange = <span class="function">(<span class="params">event</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; changeModel &#125; = <span class="keyword">this</span>.context;</span><br><span class="line">      <span class="keyword">const</span> &#123; onChange &#125; = <span class="keyword">this</span>.props;</span><br><span class="line">      <span class="keyword">const</span> &#123; v_model &#125; = <span class="keyword">this</span>.props;</span><br><span class="line">      changeModel(v_model, event.target.value);</span><br><span class="line">      <span class="keyword">if</span>(<span class="keyword">typeof</span> onChange === <span class="string">'function'</span>)&#123;onChange(event);&#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; model &#125; = <span class="keyword">this</span>.context;</span><br><span class="line">      <span class="keyword">const</span> &#123; v_model &#125; = <span class="keyword">this</span>.props;</span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span></span></span></span><br><span class="line"><span class="xml">        &#123;...this.props&#125;</span></span><br><span class="line"><span class="xml">        value=&#123;model[v_model]&#125;</span></span><br><span class="line"><span class="xml">        onChange=&#123;this.onChange&#125;</span></span><br><span class="line"><span class="xml">      /&gt;;</span></span><br><span class="line"><span class="xml">    &#125;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br><span class="line"><span class="xml">@proxyHoc</span></span><br><span class="line"><span class="xml">class Input extends Component &#123;</span></span><br><span class="line"><span class="xml">  render() &#123;</span></span><br><span class="line"><span class="xml">    return <span class="tag">&lt;<span class="name">input</span> &#123;<span class="attr">...this.props</span>&#125;&gt;</span><span class="tag">&lt;/<span class="name">input</span>&gt;</span></span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br></pre></td></tr></table></figure>
<p>上面的代码只是简略的一部分，除了<code>input</code>，我们还可以将<code>HOC</code>应用在<code>select</code>等其他表单组件，甚至还可以将上面的<code>HOC</code>兼容到<code>span、table</code>等展示组件，这样做可以大大简化代码，让我们省去了很多状态管理的工作，使用如下：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      &lt;Form &gt;</span><br><span class="line">        &lt;Input v_model=<span class="string">"name"</span>&gt;<span class="xml"><span class="tag">&lt;/<span class="name">Input</span>&gt;</span></span></span><br><span class="line">        &lt;Input v_model=<span class="string">"pwd"</span>&gt;<span class="xml"><span class="tag">&lt;/<span class="name">Input</span>&gt;</span></span></span><br><span class="line">      &lt;<span class="regexp">/Form&gt;</span></span><br><span class="line"><span class="regexp">    )</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>
<h3 id="表单校验"><a href="#表单校验" class="headerlink" title="表单校验"></a>表单校验</h3><p>基于上面的双向绑定的例子，我们再来一个表单验证器，表单验证器可以包含验证函数以及提示信息，当验证不通过时，展示错误信息：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">validateHoc</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">    <span class="keyword">constructor</span>(props) &#123;</span><br><span class="line">      <span class="keyword">super</span>(props);</span><br><span class="line">      <span class="keyword">this</span>.state = &#123; <span class="attr">error</span>: <span class="string">''</span> &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    onChange = <span class="function">(<span class="params">event</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; validator &#125; = <span class="keyword">this</span>.props;</span><br><span class="line">      <span class="keyword">if</span> (validator &amp;&amp; <span class="keyword">typeof</span> validator.func === <span class="string">'function'</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (!validator.func(event.target.value)) &#123;</span><br><span class="line">          <span class="keyword">this</span>.setState(&#123; <span class="attr">error</span>: validator.msg &#125;)</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          <span class="keyword">this</span>.setState(&#123; <span class="attr">error</span>: <span class="string">''</span> &#125;)</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span></span><br><span class="line">        &lt;WrappedComponent onChange=&#123;this.onChange&#125;  &#123;...this.props&#125; /&gt;</span><br><span class="line">        &lt;div&gt;&#123;this.state.error || ''&#125;&lt;/div&gt;</span><br><span class="line"><span class="xml">      <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml">    &#125;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> validatorName = &#123;</span><br><span class="line">  func: <span class="function">(<span class="params">val</span>) =&gt;</span> val &amp;&amp; !<span class="built_in">isNaN</span>(val),</span><br><span class="line">  msg: <span class="string">'请输入数字'</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> validatorPwd = &#123;</span><br><span class="line">  func: <span class="function">(<span class="params">val</span>) =&gt;</span> val &amp;&amp; val.length &gt; <span class="number">6</span>,</span><br><span class="line">  msg: <span class="string">'密码必须大于6位'</span></span><br><span class="line">&#125;</span><br><span class="line">&lt;HOCInput validator=&#123;validatorName&#125; v_model=<span class="string">"name"</span>&gt;<span class="xml"><span class="tag">&lt;/<span class="name">HOCInput</span>&gt;</span></span></span><br><span class="line">&lt;HOCInput validator=&#123;validatorPwd&#125; v_model=<span class="string">"pwd"</span>&gt;<span class="xml"><span class="tag">&lt;/<span class="name">HOCInput</span>&gt;</span></span></span><br></pre></td></tr></table></figure>
<p>当然，还可以在<code>Form</code>提交的时候判断所有验证器是否通过，验证器也可以设置为数组等等，由于文章篇幅原因，代码被简化了很多，有兴趣的同学可以自己实现。</p>
<h2 id="Redux的connect"><a href="#Redux的connect" class="headerlink" title="Redux的connect"></a>Redux的connect</h2><p><img src="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/hoc11.png" alt="image"></p>
<p>redux中的<code>connect</code>，其实就是一个<code>HOC</code>，下面就是一个简化版的<code>connect</code>实现：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> connect = <span class="function">(<span class="params">mapStateToProps, mapDispatchToProps</span>) =&gt;</span> (WrappedComponent) =&gt; &#123;</span><br><span class="line">  <span class="class"><span class="keyword">class</span> <span class="title">Connect</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> contextTypes = &#123;</span><br><span class="line">      store: PropTypes.object</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span> () &#123;</span><br><span class="line">      <span class="keyword">super</span>()</span><br><span class="line">      <span class="keyword">this</span>.state = &#123;</span><br><span class="line">        allProps: &#123;&#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    componentWillMount () &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; store &#125; = <span class="keyword">this</span>.context</span><br><span class="line">      <span class="keyword">this</span>._updateProps()</span><br><span class="line">      store.subscribe(<span class="function"><span class="params">()</span> =&gt;</span> <span class="keyword">this</span>._updateProps())</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    _updateProps () &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; store &#125; = <span class="keyword">this</span>.context</span><br><span class="line">      <span class="keyword">let</span> stateProps = mapStateToProps ? mapStateToProps(store.getState(), <span class="keyword">this</span>.props): &#123;&#125; </span><br><span class="line">      <span class="keyword">let</span> dispatchProps = mapDispatchToProps? mapDispatchToProps(store.dispatch, <span class="keyword">this</span>.props) : &#123;&#125; </span><br><span class="line">      <span class="keyword">this</span>.setState(&#123;</span><br><span class="line">        allProps: &#123;</span><br><span class="line">          ...stateProps,</span><br><span class="line">          ...dispatchProps,</span><br><span class="line">          ...this.props</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    render () &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> &#123;<span class="attr">...this.state.allProps</span>&#125; /&gt;</span></span></span><br><span class="line"><span class="xml">    &#125;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">  return Connect</span></span><br><span class="line"><span class="xml">&#125;</span></span><br></pre></td></tr></table></figure>
<p>代码非常清晰，<code>connect</code>函数其实就做了一件事，将<code>mapStateToProps</code>和<code>mapDispatchToProps</code>分别解构后传给原组件，这样我们在原组件内就可以直接用<code>props</code>获取<code>state</code>以及<code>dispatch</code>函数了。</p>
<h2 id="使用HOC的注意事项"><a href="#使用HOC的注意事项" class="headerlink" title="使用HOC的注意事项"></a>使用HOC的注意事项</h2><h3 id="告诫—静态属性拷贝"><a href="#告诫—静态属性拷贝" class="headerlink" title="告诫—静态属性拷贝"></a>告诫—静态属性拷贝</h3><p>当我们应用<code>HOC</code>去增强另一个组件时，我们实际使用的组件已经不是原组件了，所以我们拿不到原组件的任何静态属性，我们可以在<code>HOC</code>的结尾手动拷贝他们：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">proxyHOC</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="class"><span class="keyword">class</span> <span class="title">HOCComponent</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> &#123;<span class="attr">...this.props</span>&#125; /&gt;</span>;</span></span><br><span class="line"><span class="xml">    &#125;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">  HOCComponent.staticMethod = WrappedComponent.staticMethod;</span></span><br><span class="line"><span class="xml">  // ... </span></span><br><span class="line"><span class="xml">  return HOCComponent;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br></pre></td></tr></table></figure>
<p>如果原组件有非常多的静态属性，这个过程是非常痛苦的，而且你需要去了解需要增强的所有组件的静态属性是什么，我们可以使用<a href="https://github.com/mridgway/hoist-non-react-statics" target="_blank" rel="noopener"><code>hoist-non-react-statics</code></a>来帮助我们解决这个问题，它可以自动帮我们拷贝所有非<code>React</code>的静态方法，使用方式如下：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> hoistNonReactStatic <span class="keyword">from</span> <span class="string">'hoist-non-react-statics'</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">proxyHOC</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="class"><span class="keyword">class</span> <span class="title">HOCComponent</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> &#123;<span class="attr">...this.props</span>&#125; /&gt;</span>;</span></span><br><span class="line"><span class="xml">    &#125;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">  hoistNonReactStatic(HOCComponent,WrappedComponent);</span></span><br><span class="line"><span class="xml">  return HOCComponent;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br></pre></td></tr></table></figure>
<h3 id="告诫—传递refs"><a href="#告诫—传递refs" class="headerlink" title="告诫—传递refs"></a>告诫—传递refs</h3><p>使用高阶组件后，获取到的<code>ref</code>实际上是最外层的容器组件，而非原组件，但是很多情况下我们需要用到原组件的<code>ref</code>。</p>
<p>高阶组件并不能像透传<code>props</code>那样将<code>refs</code>透传，我们可以用一个回调函数来完成<code>ref</code>的传递：<br><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">hoc</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">    getWrappedRef = <span class="function"><span class="params">()</span> =&gt;</span> <span class="keyword">this</span>.wrappedRef;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> <span class="attr">ref</span>=<span class="string">&#123;ref</span> =&gt;</span> &#123; this.wrappedRef = ref &#125;&#125; &#123;...this.props&#125; /&gt;;</span></span><br><span class="line"><span class="xml">    &#125;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br><span class="line"><span class="xml">@hoc</span></span><br><span class="line"><span class="xml">class Input extends Component &#123;</span></span><br><span class="line"><span class="xml">  render() &#123; return <span class="tag">&lt;<span class="name">input</span>&gt;</span><span class="tag">&lt;/<span class="name">input</span>&gt;</span> &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br><span class="line"><span class="xml">class App extends Component &#123;</span></span><br><span class="line"><span class="xml">  render() &#123;</span></span><br><span class="line"><span class="xml">    return (</span></span><br><span class="line"><span class="xml">      <span class="tag">&lt;<span class="name">Input</span> <span class="attr">ref</span>=<span class="string">&#123;ref</span> =&gt;</span> &#123; this.inpitRef = ref.getWrappedRef() &#125;&#125; &gt;<span class="tag">&lt;/<span class="name">Input</span>&gt;</span></span></span><br><span class="line"><span class="xml">    );</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br></pre></td></tr></table></figure></p>
<p><code>React 16.3</code>版本提供了一个<code>forwardRef API</code>来帮助我们进行<code>refs</code>传递，这样我们在高阶组件上获取的<code>ref</code>就是原组件的<code>ref</code>了，而不需要再手动传递，如果你的<code>React</code>版本大于<code>16.3</code>，可以使用下面的方式:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">hoc</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="class"><span class="keyword">class</span> <span class="title">HOC</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; forwardedRef, ...props &#125; = <span class="keyword">this</span>.props;</span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> <span class="attr">ref</span>=<span class="string">&#123;forwardedRef&#125;</span> &#123;<span class="attr">...props</span>&#125; /&gt;</span>;</span></span><br><span class="line"><span class="xml">    &#125;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">  return React.forwardRef((props, ref) =&gt; &#123;</span></span><br><span class="line">    return &lt;HOC forwardedRef=&#123;ref&#125; &#123;...props&#125; /&gt;;</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="告诫—不要在render方法内使用高阶组件"><a href="#告诫—不要在render方法内使用高阶组件" class="headerlink" title="告诫—不要在render方法内使用高阶组件"></a>告诫—不要在render方法内使用高阶组件</h3><p><code>React</code> <code>Diff</code>算法的原则是：</p>
<ul>
<li>使用组件标识确定是卸载还是更新组件</li>
<li>如果组件的和前一次渲染时标识是相同的，递归更新子组件</li>
<li>如果标识不同卸载组件重新挂载新组件</li>
</ul>
<p>每次调用高阶组件生成的都是是一个全新的组件，组件的唯一标识响应的也会改变，如果在<code>render</code>方法调用了高阶组件，这会导致组件每次都会被卸载后重新挂载。</p>
<h3 id="约定-不要改变原始组件"><a href="#约定-不要改变原始组件" class="headerlink" title="约定-不要改变原始组件"></a>约定-不要改变原始组件</h3><p>官方文档对高阶组件的说明：</p>
<blockquote>
<p>高阶组件就是一个没有副作用的纯函数。</p>
</blockquote>
<p>我们再来看看纯函数的定义：</p>
<blockquote>
<p>如果函数的调用参数相同，则永远返回相同的结果。它不依赖于程序执行期间函数外部任何状态或数据的变化，必须只依赖于其输入参数。<br>该函数不会产生任何可观察的副作用，例如网络请求，输入和输出设备或数据突变。</p>
</blockquote>
<p>如果我们在高阶组件对原组件进行了修改，例如下面的代码：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">InputComponent.prototype.componentWillReceiveProps = <span class="function"><span class="keyword">function</span>(<span class="params">nextProps</span>) </span>&#123; ... &#125;</span><br></pre></td></tr></table></figure>
<p>这样就破坏了我们对高阶组件的约定，同时也改变了使用高阶组件的初衷：我们使用高阶组件是为了<code>增强</code>而非<code>改变</code>原组件。</p>
<h3 id="约定-透传不相关的props"><a href="#约定-透传不相关的props" class="headerlink" title="约定-透传不相关的props"></a>约定-透传不相关的props</h3><p>使用高阶组件，我们可以代理所有的<code>props</code>，但往往特定的<code>HOC</code>只会用到其中的一个或几个<code>props</code>。我们需要把其他不相关的<code>props</code>透传给原组件，如下面的代码：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">visible</span>(<span class="params">WrappedComponent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; visible, ...props &#125; = <span class="keyword">this</span>.props;</span><br><span class="line">      <span class="keyword">if</span> (visible === <span class="literal">false</span>) <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> &#123;<span class="attr">...props</span>&#125; /&gt;</span>;</span></span><br><span class="line"><span class="xml">    &#125;</span></span><br><span class="line"><span class="xml">  &#125;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br></pre></td></tr></table></figure>
<p>我们只使用<code>visible</code>属性来控制组件的显示可隐藏，把其他<code>props</code>透传下去。</p>
<h3 id="约定-displayName"><a href="#约定-displayName" class="headerlink" title="约定-displayName"></a>约定-displayName</h3><p>在使用<code>React Developer Tools</code>进行调试时，如果我们使用了<code>HOC</code>，调试界面可能变得非常难以阅读，如下面的代码：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">@visible</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Show</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>我是一个标签<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">@visible</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Title</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>我是一个标题<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><img src="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/hoc5.png" alt="image"></p>
<p>为了方便调试，我们可以手动为<code>HOC</code>指定一个<code>displayName</code>，官方推荐使用<code>HOCName(WrappedComponentName)</code>：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">static</span> displayName = <span class="string">`Visible(<span class="subst">$&#123;WrappedComponent.displayName&#125;</span>)`</span></span><br></pre></td></tr></table></figure>
<p><img src="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/hoc6.png" alt="image"></p>
<p>这个约定帮助确保高阶组件最大程度的灵活性和可重用性。</p>
<h2 id="使用HOC的动机"><a href="#使用HOC的动机" class="headerlink" title="使用HOC的动机"></a>使用HOC的动机</h2><p>回顾下上文提到的 <code>Mixin</code> 带来的风险：</p>
<ul>
<li><code>Mixin</code> 可能会相互依赖，相互耦合，不利于代码维护</li>
<li>不同的<code>Mixin</code>中的方法可能会相互冲突</li>
<li><code>Mixin</code>非常多时，组件是可以感知到的，甚至还要为其做相关处理，这样会给代码造成滚雪球式的复杂性</li>
</ul>
<p><img src="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/hoc7.png" alt="image"></p>
<p>而<code>HOC</code>的出现可以解决这些问题：</p>
<ul>
<li>高阶组件就是一个没有副作用的纯函数，各个高阶组件不会互相依赖耦合</li>
<li>高阶组件也有可能造成冲突，但我们可以在遵守约定的情况下避免这些行为</li>
<li>高阶组件并不关心数据使用的方式和原因，而被包裹的组件也不关心数据来自何处。高阶组件的增加不会为原组件增加负担</li>
</ul>
<h2 id="HOC的缺陷"><a href="#HOC的缺陷" class="headerlink" title="HOC的缺陷"></a>HOC的缺陷</h2><ul>
<li><code>HOC</code>需要在原组件上进行包裹或者嵌套，如果大量使用<code>HOC</code>，将会产生非常多的嵌套，这让调试变得非常困难。</li>
<li><code>HOC</code>可以劫持<code>props</code>，在不遵守约定的情况下也可能造成冲突。</li>
</ul>
<h2 id="Hooks"><a href="#Hooks" class="headerlink" title="Hooks"></a>Hooks</h2><p><img src="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/hoc10.png" alt="image"></p>
<p><code>Hooks</code>是<code>React v16.7.0-alpha</code>中加入的新特性。它可以让你在<code>class</code>以外使用<code>state</code>和其他<code>React</code>特性。</p>
<p>使用<code>Hooks</code>，你可以在将含有<code>state</code>的逻辑从组件中抽象出来，这将可以让这些逻辑容易被测试。同时，<code>Hooks</code>可以帮助你在不重写组件结构的情况下复用这些逻辑。所以，它也可以作为一种实现<code>状态逻辑复用</code>的方案。</p>
<p>阅读下面的章节<a href="#使用Hook的动机">使用Hook的动机</a>你可以发现，它可以同时解决<code>Mixin</code>和<code>HOC</code>带来的问题。</p>
<h2 id="官方提供的Hooks"><a href="#官方提供的Hooks" class="headerlink" title="官方提供的Hooks"></a>官方提供的Hooks</h2><h3 id="State-Hook"><a href="#State-Hook" class="headerlink" title="State Hook"></a>State Hook</h3><p>我们要使用<code>class</code>组件实现一个<code>计数器</code>功能，我们可能会这样写：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> <span class="title">Count</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(props) &#123;</span><br><span class="line">    <span class="keyword">super</span>(props);</span><br><span class="line">    <span class="keyword">this</span>.state = &#123; <span class="attr">count</span>: <span class="number">0</span> &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      &lt;div&gt;</span><br><span class="line">        &lt;p&gt;You clicked &#123;<span class="keyword">this</span>.state.count&#125; times&lt;<span class="regexp">/p&gt;</span></span><br><span class="line"><span class="regexp">        &lt;button onClick=&#123;() =&gt; &#123; this.setState(&#123; count: this.state.count + 1 &#125;) &#125;&#125;&gt;</span></span><br><span class="line"><span class="regexp">          Click me</span></span><br><span class="line"><span class="regexp">        &lt;/</span>button&gt;</span><br><span class="line">      &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">    )</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>
<p>通过<code>useState</code>，我们使用函数式组件也能实现这样的功能：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">HookTest</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> [count, setCount] = useState(<span class="number">0</span>);</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;p&gt;You clicked &#123;count&#125; times&lt;<span class="regexp">/p&gt;</span></span><br><span class="line"><span class="regexp">      &lt;button onClick=&#123;() =&gt; &#123; setCount(count + 1); setNumber(number + 1); &#125;&#125;&gt;</span></span><br><span class="line"><span class="regexp">        Click me</span></span><br><span class="line"><span class="regexp">        &lt;/</span>button&gt;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>
<p><code>useState</code>是一个钩子，他可以为函数式组件增加一些状态，并且提供改变这些状态的函数，同时它接收一个参数，这个参数作为状态的默认值。</p>
<h3 id="Effect-Hook"><a href="#Effect-Hook" class="headerlink" title="Effect Hook"></a>Effect Hook</h3><blockquote>
<p>Effect Hook 可以让你在函数组件中执行一些具有 side effect（副作用）的操作</p>
</blockquote>
<p><strong>参数</strong></p>
<p><code>useEffect</code>方法接收传入两个参数：</p>
<ul>
<li>1.回调函数：在第组件一次<code>render</code>和之后的每次<code>update</code>后运行，<code>React</code>保证在<code>DOM</code>已经更新完成之后才会运行回调。</li>
<li>2.状态依赖(数组)：当配置了状态依赖项后，只有检测到配置的状态变化时，才会调用回调函数。</li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">useEffect(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 只要组件render后就会执行</span></span><br><span class="line">&#125;);</span><br><span class="line">useEffect(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 只有count改变时才会执行</span></span><br><span class="line">&#125;,[count]);</span><br></pre></td></tr></table></figure>
<p><strong>回调返回值</strong></p>
<p><code>useEffect</code>的第一个参数可以返回一个函数，当页面渲染了下一次更新的结果后，执行下一次<code>useEffect</code>之前，会调用这个函数。这个函数常常用来对上一次调用<code>useEffect</code>进行清理。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">HookTest</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> [count, setCount] = useState(<span class="number">0</span>);</span><br><span class="line">  useEffect(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'执行...'</span>, count);</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">'清理...'</span>, count);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;, [count]);</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;p&gt;You clicked &#123;count&#125; times&lt;<span class="regexp">/p&gt;</span></span><br><span class="line"><span class="regexp">      &lt;button onClick=&#123;() =&gt; &#123; setCount(count + 1); setNumber(number + 1); &#125;&#125;&gt;</span></span><br><span class="line"><span class="regexp">        Click me</span></span><br><span class="line"><span class="regexp">        &lt;/</span>button&gt;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure>
<p>执行上面的代码，并点击几次按钮，会得到下面的结果：</p>
<p><img src="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/hook1.png" alt="image"></p>
<p>注意，如果加上浏览器渲染的情况，结果应该是这样的：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">页面渲染..<span class="number">.1</span></span><br><span class="line">执行... <span class="number">1</span></span><br><span class="line">页面渲染..<span class="number">.2</span></span><br><span class="line">清理... <span class="number">1</span></span><br><span class="line">执行... <span class="number">2</span></span><br><span class="line">页面渲染..<span class="number">.3</span></span><br><span class="line">清理... <span class="number">2</span></span><br><span class="line">执行... <span class="number">3</span></span><br><span class="line">页面渲染..<span class="number">.4</span></span><br><span class="line">清理... <span class="number">3</span></span><br><span class="line">执行... <span class="number">4</span></span><br></pre></td></tr></table></figure>
<p>那么为什么在浏览器渲染完后，再执行清理的方法还能找到上次的<code>state</code>呢？原因很简单，我们在<code>useEffect</code>中返回的是一个函数，这形成了一个闭包，这能保证我们上一次执行函数存储的变量不被销毁和污染。</p>
<p>你可以尝试下面的代码可能更好理解</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> flag = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">var</span> clean;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">effect</span>(<span class="params">flag</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(flag);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">clean = effect(flag);</span><br><span class="line">flag = <span class="number">2</span>;</span><br><span class="line">clean();</span><br><span class="line">clean = effect(flag);</span><br><span class="line">flag = <span class="number">3</span>;</span><br><span class="line">clean();</span><br><span class="line">clean = effect(flag);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 执行结果</span></span><br><span class="line"></span><br><span class="line">effect... <span class="number">1</span></span><br><span class="line">clean... <span class="number">1</span></span><br><span class="line">effect... <span class="number">2</span></span><br><span class="line">clean... <span class="number">2</span></span><br><span class="line">effect... <span class="number">3</span></span><br></pre></td></tr></table></figure>
<p><strong>模拟componentDidMount</strong></p>
<p><code>componentDidMount</code>等价于<code>useEffect</code>的回调仅在页面初始化完成后执行一次，当<code>useEffect</code>的第二个参数传入一个空数组时可以实现这个效果。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">useDidMount</span>(<span class="params">callback</span>) </span>&#123;</span><br><span class="line">  useEffect(callback, []);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>官方不推荐上面这种写法，因为这有可能导致一些错误。</p>
</blockquote>
<p><strong>模拟componentWillUnmount</strong></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">useUnMount</span>(<span class="params">callback</span>) </span>&#123;</span><br><span class="line">  useEffect(<span class="function"><span class="params">()</span> =&gt;</span> callback, []);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>不像 componentDidMount 或者 componentDidUpdate，useEffect 中使用的 effect 并不会阻滞浏览器渲染页面。这让你的 app 看起来更加流畅。</p>
</blockquote>
<h3 id="ref-Hook"><a href="#ref-Hook" class="headerlink" title="ref Hook"></a>ref Hook</h3><p>使用<code>useRef Hook</code>，你可以轻松的获取到<code>dom</code>的<code>ref</code>。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">Input</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> inputEl = useRef(<span class="literal">null</span>);</span><br><span class="line">  <span class="keyword">const</span> onButtonClick = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    inputEl.current.focus();</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;input ref=&#123;inputEl&#125; type=<span class="string">"text"</span> /&gt;</span><br><span class="line">      &lt;button onClick=&#123;onButtonClick&#125;&gt;Focus the input&lt;<span class="regexp">/button&gt;</span></span><br><span class="line"><span class="regexp">    &lt;/</span>div&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>注意<code>useRef()</code>并不仅仅可以用来当作获取<code>ref</code>使用，使用<code>useRef</code>产生的<code>ref</code>的<code>current</code>属性是可变的，这意味着你可以用它来保存一个任意值。</p>
<p><strong>模拟componentDidUpdate</strong></p>
<p><code>componentDidUpdate</code>就相当于除去第一次调用的<code>useEffect</code>，我们可以借助<code>useRef</code>生成一个标识，来记录是否为第一次执行：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">useDidUpdate</span>(<span class="params">callback, prop</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> init = useRef(<span class="literal">true</span>);</span><br><span class="line">  useEffect(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (init.current) &#123;</span><br><span class="line">      init.current = <span class="literal">false</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> callback();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;, prop);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="使用Hook的注意事项"><a href="#使用Hook的注意事项" class="headerlink" title="使用Hook的注意事项"></a>使用Hook的注意事项</h2><h3 id="使用范围"><a href="#使用范围" class="headerlink" title="使用范围"></a>使用范围</h3><ul>
<li>只能在<code>React</code>函数式组件或自定义<code>Hook</code>中使用<code>Hook</code>。</li>
</ul>
<p><code>Hook</code>的提出主要就是为了解决<code>class</code>组件的一系列问题，所以我们能在<code>class</code>组件中使用它。 </p>
<h3 id="声明约束"><a href="#声明约束" class="headerlink" title="声明约束"></a>声明约束</h3><ul>
<li>不要在循环，条件或嵌套函数中调用Hook。</li>
</ul>
<p><code>Hook</code>通过数组实现的，每次<code>useState</code> 都会改变下标，<code>React</code>需要利用调用顺序来正确更新相应的状态，如果<code>useState</code> 被包裹循环或条件语句中，那每就可能会引起调用顺序的错乱，从而造成意想不到的错误。</p>
<p>我们可以安装一个<code>eslint</code>插件来帮助我们避免这些问题。<br><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 安装</span></span><br><span class="line">npm install eslint-plugin-react-hooks --save-dev</span><br><span class="line"><span class="comment">// 配置</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">"plugins"</span>: [</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="string">"react-hooks"</span></span><br><span class="line">  ],</span><br><span class="line">  <span class="string">"rules"</span>: &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="string">"react-hooks/rules-of-hooks"</span>: <span class="string">"error"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="自定义Hook"><a href="#自定义Hook" class="headerlink" title="自定义Hook"></a>自定义Hook</h2><p>像上面介绍的<code>HOC</code>和<code>mixin</code>一样，我们同样可以通过自定义的<code>Hook</code>将组件中类似的状态逻辑抽取出来。</p>
<p>自定义<code>Hook</code>非常简单，我们只需要定义一个函数，并且把相应需要的状态和<code>effect</code>封装进去，同时，<code>Hook</code>之间也是可以相互引用的。使用<code>use</code>开头命名自定义<code>Hook</code>，这样可以方便<code>eslint</code>进行检查。</p>
<p>下面我们看几个具体的<code>Hook</code>封装：</p>
<h3 id="日志打点-1"><a href="#日志打点-1" class="headerlink" title="日志打点"></a>日志打点</h3><p>我们可以使用上面封装的生命周期<code>Hook</code>。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> useLogger = <span class="function">(<span class="params">componentName, ...params</span>) =&gt;</span> &#123;</span><br><span class="line">  useDidMount(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">`<span class="subst">$&#123;componentName&#125;</span>初始化`</span>, ...params);</span><br><span class="line">  &#125;);</span><br><span class="line">  useUnMount(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">`<span class="subst">$&#123;componentName&#125;</span>卸载`</span>, ...params);</span><br><span class="line">  &#125;)</span><br><span class="line">  useDidUpdate(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">`<span class="subst">$&#123;componentName&#125;</span>更新`</span>, ...params);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Page1</span>(<span class="params">props</span>)</span>&#123;</span><br><span class="line">  useLogger(<span class="string">'Page1'</span>,props);</span><br><span class="line">  <span class="keyword">return</span> (<span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>...<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="修改title"><a href="#修改title" class="headerlink" title="修改title"></a>修改title</h3><p>根据不同的页面名称修改页面<code>title</code>:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">useTitle</span>(<span class="params">title</span>) </span>&#123;</span><br><span class="line">  useEffect(</span><br><span class="line">    () =&gt; &#123;</span><br><span class="line">      <span class="built_in">document</span>.title = title;</span><br><span class="line">      <span class="keyword">return</span> <span class="function"><span class="params">()</span> =&gt;</span> (<span class="built_in">document</span>.title = <span class="string">"主页"</span>);</span><br><span class="line">    &#125;,</span><br><span class="line">    [title]</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Page1</span>(<span class="params">props</span>)</span>&#123;</span><br><span class="line">  useTitle(<span class="string">'Page1'</span>);</span><br><span class="line">  <span class="keyword">return</span> (<span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>...<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="双向绑定-1"><a href="#双向绑定-1" class="headerlink" title="双向绑定"></a>双向绑定</h3><p>我们将表单<code>onChange</code>的逻辑抽取出来封装成一个<code>Hook</code>，这样所有需要进行双向绑定的表单组件都可以进行复用：<br><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">useBind</span>(<span class="params">init</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> [value, setValue] = useState(init);</span><br><span class="line">  <span class="keyword">let</span> onChange = useCallback(<span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>&#123;</span><br><span class="line">    setValue(event.currentTarget.value);</span><br><span class="line">  &#125;, []);</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    value,</span><br><span class="line">    onChange</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Page1</span>(<span class="params">props</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">let</span> value = useBind(<span class="string">''</span>);</span><br><span class="line">  <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">input</span> &#123;<span class="attr">...value</span>&#125; /&gt;</span>;</span></span><br><span class="line"><span class="xml">&#125;</span></span><br></pre></td></tr></table></figure></p>
<p>当然，你可以向上面的<code>HOC</code>那样，结合<code>context</code>和<code>form</code>来封装一个更通用的双向绑定，有兴趣可以手动实现一下。</p>
<h2 id="使用Hook的动机"><a href="#使用Hook的动机" class="headerlink" title="使用Hook的动机"></a>使用Hook的动机</h2><h3 id="减少状态逻辑复用的风险"><a href="#减少状态逻辑复用的风险" class="headerlink" title="减少状态逻辑复用的风险"></a>减少状态逻辑复用的风险</h3><p><code>Hook</code>和<code>Mixin</code>在用法上有一定的相似之处，但是<code>Mixin</code>引入的逻辑和状态是可以相互覆盖的，而多个<code>Hook</code>之间互不影响，这让我们不需要在把一部分精力放在防止避免逻辑复用的冲突上。</p>
<p>在不遵守约定的情况下使用<code>HOC</code>也有可能带来一定冲突，比如<code>props</code>覆盖等等，使用<code>Hook</code>则可以避免这些问题。</p>
<h3 id="避免地狱式嵌套"><a href="#避免地狱式嵌套" class="headerlink" title="避免地狱式嵌套"></a>避免地狱式嵌套</h3><p>大量使用<code>HOC</code>的情况下让我们的代码变得嵌套层级非常深，使用<code>HOC</code>，我们可以实现扁平式的状态逻辑复用，而避免了大量的组件嵌套。</p>
<h3 id="让组件更容易理解"><a href="#让组件更容易理解" class="headerlink" title="让组件更容易理解"></a>让组件更容易理解</h3><p>在使用<code>class</code>组件构建我们的程序时，他们各自拥有自己的状态，业务逻辑的复杂使这些组件变得越来越庞大，各个生命周期中会调用越来越多的逻辑，越来越难以维护。使用<code>Hook</code>，可以让你更大限度的将公用逻辑抽离，将一个组件分割成更小的函数，而不是强制基于生命周期方法进行分割。</p>
<h3 id="使用函数代替class"><a href="#使用函数代替class" class="headerlink" title="使用函数代替class"></a>使用函数代替class</h3><p>相比函数，编写一个<code>class</code>可能需要掌握更多的知识，需要注意的点也越多，比如<code>this</code>指向、绑定事件等等。另外，计算机理解一个<code>class</code>比理解一个函数更快。<code>Hooks</code>让你可以在<code>classes</code>之外使用更多<code>React</code>的新特性。</p>
<h2 id="理性的选择"><a href="#理性的选择" class="headerlink" title="理性的选择"></a>理性的选择</h2><p>实际上，<code>Hook</code>在<code>react 16.8.0</code>才正式发布<code>Hook</code>稳定版本，笔者也还未在生产环境下使用，目前笔者在生产环境下使用的最多的是<code>HOC</code>。</p>
<p><code>React</code>官方完全没有把<code>classes</code>从<code>React</code>中移除的打算，<code>class</code>组件和<code>Hook</code>完全可以同时存在，官方也建议避免任何“大范围重构”，毕竟这是一个非常新的版本，如果你喜欢它，可以在新的非关键性的代码中使用<code>Hook</code>。</p>
<h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p><code>mixin</code>已被抛弃，<code>HOC</code>正当壮年，<code>Hook</code>初露锋芒，前端圈就是这样，技术迭代速度非常之快，但我们在学习这些知识之时一定要明白为什么要学，学了有没有用，要不要用。不忘初心，方得始终。</p>
<p>文中如有错误，欢迎在评论区指正，谢谢阅读。</p>

        </div>
        <footer class="article-footer">
            



    <a data-url="https://www.lisq.xyz/2019/04/09/【React深入】从Mixin到HOC再到Hook/" data-id="cjv0hxjyd000bnh0dxwac8fv8" class="article-share-link"><i class="fa fa-share"></i>分享到</a>
<script>
    (function ($) {
        $('body').on('click', function() {
            $('.article-share-box.on').removeClass('on');
        }).on('click', '.article-share-link', function(e) {
            e.stopPropagation();

            var $this = $(this),
                url = $this.attr('data-url'),
                encodedUrl = encodeURIComponent(url),
                id = 'article-share-box-' + $this.attr('data-id'),
                offset = $this.offset(),
                box;

            if ($('#' + id).length) {
                box = $('#' + id);

                if (box.hasClass('on')){
                    box.removeClass('on');
                    return;
                }
            } else {
                var html = [
                    '<div id="' + id + '" class="article-share-box">',
                        '<input class="article-share-input" value="' + url + '">',
                        '<div class="article-share-links">',
                            '<a href="https://twitter.com/intent/tweet?url=' + encodedUrl + '" class="article-share-twitter" target="_blank" title="Twitter"></a>',
                            '<a href="https://www.facebook.com/sharer.php?u=' + encodedUrl + '" class="article-share-facebook" target="_blank" title="Facebook"></a>',
                            '<a href="http://pinterest.com/pin/create/button/?url=' + encodedUrl + '" class="article-share-pinterest" target="_blank" title="Pinterest"></a>',
                            '<a href="https://plus.google.com/share?url=' + encodedUrl + '" class="article-share-google" target="_blank" title="Google+"></a>',
                        '</div>',
                    '</div>'
                ].join('');

              box = $(html);

              $('body').append(box);
            }

            $('.article-share-box.on').hide();

            box.css({
                top: offset.top + 25,
                left: offset.left
            }).addClass('on');

        }).on('click', '.article-share-box', function (e) {
            e.stopPropagation();
        }).on('click', '.article-share-box-input', function () {
            $(this).select();
        }).on('click', '.article-share-box-link', function (e) {
            e.preventDefault();
            e.stopPropagation();

            window.open(this.href, 'article-share-box-window-' + Date.now(), 'width=500,height=450');
        });
    })(jQuery);
</script>

        </footer>
    </div>
</article>


    <section id="comments">
    
        
    <div id="disqus_thread">
        <noscript>Please enable JavaScript to view the <a href="//disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
    </div>

    
    </section>





<div id="roott"></div>

<button id="tocbtn">打开目录</button>


<script>
    var showToc = false;
    document.getElementById('tocbtn').onclick = function () {
        if (showToc) {
            document.getElementById('roott').innerHTML = ''
            document.getElementById('tocbtn').innerHTML = '打开目录'
        } else {
            document.getElementById('roott').innerHTML = `<aside id="article-toc" role="navigation" class="fixed">
  <div id="article-toc-inner">
    <strong class="sidebar-title">
      目录</strong>
    <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#导读"><span class="toc-text">导读</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Mixin设计模式"><span class="toc-text">Mixin设计模式</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#React中应用Mixin"><span class="toc-text">React中应用Mixin</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Mixin带来的危害"><span class="toc-text">Mixin带来的危害</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#装饰模式"><span class="toc-text">装饰模式</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#高阶组件（HOC）"><span class="toc-text">高阶组件（HOC）</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#HOC的实现方式"><span class="toc-text">HOC的实现方式</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#属性代理"><span class="toc-text">属性代理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#反向继承"><span class="toc-text">反向继承</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#HOC可以实现什么功能"><span class="toc-text">HOC可以实现什么功能</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#组合渲染"><span class="toc-text">组合渲染</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#条件渲染"><span class="toc-text">条件渲染</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#操作props"><span class="toc-text">操作props</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#获取refs"><span class="toc-text">获取refs</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#状态管理"><span class="toc-text">状态管理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#操作state"><span class="toc-text">操作state</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#渲染劫持"><span class="toc-text">渲染劫持</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#如何使用HOC"><span class="toc-text">如何使用HOC</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#compose"><span class="toc-text">compose</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Decorators"><span class="toc-text">Decorators</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#HOC的实际应用"><span class="toc-text">HOC的实际应用</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#日志打点"><span class="toc-text">日志打点</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#可用、权限控制"><span class="toc-text">可用、权限控制</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#双向绑定"><span class="toc-text">双向绑定</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#表单校验"><span class="toc-text">表单校验</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Redux的connect"><span class="toc-text">Redux的connect</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#使用HOC的注意事项"><span class="toc-text">使用HOC的注意事项</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#告诫—静态属性拷贝"><span class="toc-text">告诫—静态属性拷贝</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#告诫—传递refs"><span class="toc-text">告诫—传递refs</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#告诫—不要在render方法内使用高阶组件"><span class="toc-text">告诫—不要在render方法内使用高阶组件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#约定-不要改变原始组件"><span class="toc-text">约定-不要改变原始组件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#约定-透传不相关的props"><span class="toc-text">约定-透传不相关的props</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#约定-displayName"><span class="toc-text">约定-displayName</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#使用HOC的动机"><span class="toc-text">使用HOC的动机</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#HOC的缺陷"><span class="toc-text">HOC的缺陷</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Hooks"><span class="toc-text">Hooks</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#官方提供的Hooks"><span class="toc-text">官方提供的Hooks</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#State-Hook"><span class="toc-text">State Hook</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Effect-Hook"><span class="toc-text">Effect Hook</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ref-Hook"><span class="toc-text">ref Hook</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#使用Hook的注意事项"><span class="toc-text">使用Hook的注意事项</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#使用范围"><span class="toc-text">使用范围</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#声明约束"><span class="toc-text">声明约束</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#自定义Hook"><span class="toc-text">自定义Hook</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#日志打点-1"><span class="toc-text">日志打点</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#修改title"><span class="toc-text">修改title</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#双向绑定-1"><span class="toc-text">双向绑定</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#使用Hook的动机"><span class="toc-text">使用Hook的动机</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#减少状态逻辑复用的风险"><span class="toc-text">减少状态逻辑复用的风险</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#避免地狱式嵌套"><span class="toc-text">避免地狱式嵌套</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#让组件更容易理解"><span class="toc-text">让组件更容易理解</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用函数代替class"><span class="toc-text">使用函数代替class</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#理性的选择"><span class="toc-text">理性的选择</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#小结"><span class="toc-text">小结</span></a></li></ol>
  </div>
</aside>`
            document.getElementById('tocbtn').innerHTML = '关闭目录'
        }
        showToc = !showToc;
    }
</script>
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src='//unpkg.com/valine/dist/Valine.min.js'></script>
<div id="vcomments" style="padding: 30px;"></div>
<script>
    new Valine({
        el: '#vcomments',
        appId: 's1xL54gwvPRsIy3ciItiPhyD-gzGzoHsz',
        appKey: 'CmP8stKpHUHtp5abbTxpqc4H'
    })
</script>
                        </div>
                    </section>
                    <aside id="sidebar">
    <a class="sidebar-toggle" title="Expand Sidebar"><i class="toggle icon"></i></a>
    <div class="sidebar-top">
        <p>
            关注我 :</p>
        <ul class="social-links">
            
            
            <li>
                <a class="social-tooltip" title="github" href="https://github.com/ConardLi"
                    target="_blank" rel="noopener">
                    <i class="icon fa fa-github"></i>
                </a>
            </li>
            
            
        </ul>
    </div>
    
    
<nav id="article-nav">
    
        <a href="/2019/04/17/【React深入】深入分析虚拟DOM的渲染原理和特性/" id="article-nav-newer" class="article-nav-link-wrap">
        <strong class="article-nav-caption">下一篇</strong>
        <p class="article-nav-title">
        
            【React深入】深入分析虚拟DOM的渲染原理和特性
        
        </p>
        <i class="icon fa fa-chevron-right" id="icon-chevron-right"></i>
    </a>
    
    
        <a href="/2019/03/28/【浏览器和网络】全面分析前端的网络请求方式/" id="article-nav-older" class="article-nav-link-wrap">
        <strong class="article-nav-caption">上一篇</strong>
        <p class="article-nav-title">全面分析前端的网络请求方式</p>
        <i class="icon fa fa-chevron-left" id="icon-chevron-left"></i>
        </a>
    
</nav>

    
    <div class="widgets-container" style="background-color: #fff; border-right: 1px solid #e2e2e2;">
        
        
        
<div class="widget-wrap widget-list">
    <h3 class="widget-title">
        微信公众号
    </h3>
    <div class="widget">
        <img src="https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/gzh/qrcode.jpg" alt="">
    </div>
</div>

        
        

        
        
    <div class="widget-wrap">
        <h3 class="widget-title">最新文章</h3>
        <div class="widget">
            <ul id="recent-post" class="">
                
                    <li>
                        
                        <div class="item-thumbnail">
                            <a href="/2019/04/26/【自检】前端知识清单/" class="thumbnail">
    
    
        <span style="background-image:url(https://lsqimg-1257917459.cos.ap-beijing.myqcloud.com/blog/知识体系.png)" alt="【自检】前端知识清单" class="thumbnail-image"></span>
    
    
</a>

                        </div>
                        
                        <div class="item-inner">
                            <p class="item-category"></p>
                            <p class="item-title"><a href="/2019/04/26/【自检】前端知识清单/" class="title">【自检】前端知识清单</a></p>
                            <p class="item-date"><time datetime="2019-04-26T01:11:22.000Z" itemprop="datePublished">2019-04-26</time></p>
                        </div>
                    </li>
                
                    <li>
                        
                        <div class="item-thumbnail">
                            <a href="/2019/04/17/【React深入】深入分析虚拟DOM的渲染原理和特性/" class="thumbnail">
    
    
        <span style="background-image:url(https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/react11.png)" alt="【React深入】深入分析虚拟DOM的渲染原理和特性" class="thumbnail-image"></span>
    
    
</a>

                        </div>
                        
                        <div class="item-inner">
                            <p class="item-category"></p>
                            <p class="item-title"><a href="/2019/04/17/【React深入】深入分析虚拟DOM的渲染原理和特性/" class="title">【React深入】深入分析虚拟DOM的渲染原理和特性</a></p>
                            <p class="item-date"><time datetime="2019-04-17T03:15:27.000Z" itemprop="datePublished">2019-04-17</time></p>
                        </div>
                    </li>
                
                    <li>
                        
                        <div class="item-thumbnail">
                            <a href="/2019/04/09/【React深入】从Mixin到HOC再到Hook/" class="thumbnail">
    
    
        <span style="background-image:url(https://lsqimg-1257917459.cos-website.ap-beijing.myqcloud.com/blog/hoc9.png)" alt="【React深入】从Mixin到HOC再到Hook" class="thumbnail-image"></span>
    
    
</a>

                        </div>
                        
                        <div class="item-inner">
                            <p class="item-category"></p>
                            <p class="item-title"><a href="/2019/04/09/【React深入】从Mixin到HOC再到Hook/" class="title">【React深入】从Mixin到HOC再到Hook</a></p>
                            <p class="item-date"><time datetime="2019-04-09T15:27:27.000Z" itemprop="datePublished">2019-04-09</time></p>
                        </div>
                    </li>
                
                    <li>
                        
                        <div class="item-thumbnail">
                            <a href="/2019/03/28/【浏览器和网络】全面分析前端的网络请求方式/" class="thumbnail">
    
    
        <span style="background-image:url(/img/network.jpg)" alt="全面分析前端的网络请求方式" class="thumbnail-image"></span>
    
    
</a>

                        </div>
                        
                        <div class="item-inner">
                            <p class="item-category"></p>
                            <p class="item-title"><a href="/2019/03/28/【浏览器和网络】全面分析前端的网络请求方式/" class="title">全面分析前端的网络请求方式</a></p>
                            <p class="item-date"><time datetime="2019-03-28T12:38:59.000Z" itemprop="datePublished">2019-03-28</time></p>
                        </div>
                    </li>
                
                    <li>
                        
                        <div class="item-thumbnail">
                            <a href="/2019/03/12/【前端工程】前端开发者必备的nginx知识/" class="thumbnail">
    
    
        <span style="background-image:url(/img/nginx.jpg)" alt="前端开发者必备的nginx知识" class="thumbnail-image"></span>
    
    
</a>

                        </div>
                        
                        <div class="item-inner">
                            <p class="item-category"></p>
                            <p class="item-title"><a href="/2019/03/12/【前端工程】前端开发者必备的nginx知识/" class="title">前端开发者必备的nginx知识</a></p>
                            <p class="item-date"><time datetime="2019-03-12T06:50:35.000Z" itemprop="datePublished">2019-03-12</time></p>
                        </div>
                    </li>
                
            </ul>
        </div>
    </div>

        
        
<div class="widget-wrap widget-list">
    <div class="">
        <h3 class="widget-title">标签</h3>
        <div class="widget widget-wrap-inn">
            <ul class="tag-list"><li class="tag-list-item"><a class="tag-list-link" href="/tags/学习提升/">-学习提升</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/JavaScript/">JavaScript</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/React/">React</a><span class="tag-list-count">5</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/electron/">electron</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nginx/">nginx</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/博客搭建/">博客搭建</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/性能优化/">性能优化</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/浏览器和网络/">浏览器和网络</a><span class="tag-list-count">1</span></li></ul>
        </div>
    </div>
</div>


<style>
    .widget-wrap-inn {
        background-color: white;
        padding: 10px;
        border-radius: 5px;
        border: 1px solid #ddd;
    }
</style>
        
        
<div class="widget-wrap widget-list">
    <h3 class="widget-title">归档</h3>
    <div class="widget  widget-wrap-inn">
        <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/04/">四月 2019</a><span class="archive-list-count">3</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/03/">三月 2019</a><span class="archive-list-count">5</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/12/">十二月 2018</a><span class="archive-list-count">2</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/11/">十一月 2018</a><span class="archive-list-count">3</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/01/">一月 2018</a><span class="archive-list-count">1</span></li></ul>
    </div>
</div>

        
        
<div class="widget-wrap widget-list">
    <h3 class="widget-title">友情链接</h3>
    <div class="widget  widget-wrap-inn">
        <ul>
            
            <li>
                <a href="https://juejin.im/user/5bea27965188250edf4ad8b7">掘金</a>
            </li>
            
            <li>
                <a href="https://segmentfault.com/u/conardli">segmentfault</a>
            </li>
            
            <li>
                <a href="https://github.com/ConardLi">github</a>
            </li>
            
            <li>
                <a href="https://github.com/ConardLi/tpanorama">tpanorama</a>
            </li>
            
            <li>
                <a href="http://chuangzaoshi.com/">创造狮导航</a>
            </li>
            
        </ul>
    </div>
</div>

        
        
<div class="widget-wrap widget-list">
    <!-- <h3 class="widget-title">
        腾讯云专属福利
    </h3> -->
    <div class="widget">
        <a target="_blank"
            href="https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=1a3e7b07487819e129852ba7a4543262&from=console">
            <img src="/img/txy.jpg" id="txy" alt="">
        </a>
    </div>
</div>



<style>
    #txy {
        width: 280px;
        border: 5px;
    }
</style>
        
        
<div class="widget-wrap widget-list">
    <div class="widget">
        <a target="_blank" href="https://promotion.aliyun.com/ntms/act/qwbk.html?userCode=y3kv7eoj">
            <img src="/img/aly.png" id="txy" alt="">
        </a>
    </div>
</div>



<style>
    #txy {
        width: 280px;
        border: 5px;
    }
</style>
        
        
    </div>
</aside>
                </div>
            </div>
        </div>
        <footer id="footer">
    <div class="container">
        <div class="container-inner">
            <a id="back-to-top" href="javascript:;"><i class="icon fa fa-angle-up"></i></a>
            <div class="credit">
                <h1 class="logo-wrap">
                    <a href="/" class="logo"></a>
                </h1>
                <p>&copy;
                    2019
                    ConardLi
                </p>
                <p>Powered by <a href="//hexo.io/" target="_blank">Hexo</a>. Theme by <a href="//github.com/ppoffice"
                        target="_blank">ConardLi</a></p>
                <script type="text/javascript">
                    var cnzz_protocol = (("https:" == document.location.protocol) ? " https://" : " http://");
                    document.write(unescape("%3Cspan id='cnzz_stat_icon_1275088352'%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "s22.cnzz.com/z_stat.php%3Fid%3D1275088352%26show%3Dpic' type='text/javascript'%3E%3C/script%3E"));
                </script>
                <span id="busuanzi_container_site_pv" style="margin-left:50px;">
                    本站总访问量<span id="busuanzi_value_site_pv"></span>次
                </span>
                <span id="busuanzi_container_site_uv" style="margin-left:50px;">
                    本站访客数<span id="busuanzi_value_site_uv"></span>人次
                </span>
            </div>
            <div class="footer-plugins">
                
    


            </div>
        </div>
    </div>
</footer>
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
        
    
    <script>
    var disqus_shortname = 'hexo-theme-hueman';
    
    
    var disqus_url = 'https://www.lisq.xyz/2019/04/09/【React深入】从Mixin到HOC再到Hook/';
    
    (function() {
    var dsq = document.createElement('script');
    dsq.type = 'text/javascript';
    dsq.async = true;
    dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
    (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
    })();
    </script>




    
        <script src="/libs/lightgallery/js/lightgallery.min.js"></script>
        <script src="/libs/lightgallery/js/lg-thumbnail.min.js"></script>
        <script src="/libs/lightgallery/js/lg-pager.min.js"></script>
        <script src="/libs/lightgallery/js/lg-autoplay.min.js"></script>
        <script src="/libs/lightgallery/js/lg-fullscreen.min.js"></script>
        <script src="/libs/lightgallery/js/lg-zoom.min.js"></script>
        <script src="/libs/lightgallery/js/lg-hash.min.js"></script>
        <script src="/libs/lightgallery/js/lg-share.min.js"></script>
        <script src="/libs/lightgallery/js/lg-video.min.js"></script>
    
    
        <script src="/libs/justified-gallery/jquery.justifiedGallery.min.js"></script>
    
    



<!-- Custom Scripts -->
<script src="/js/main.js"></script>

    </div>
</body>

</html>